#define _DEFAULT_SOURCE
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

#define N_DWARFS 7

static const char *dwarfs[] = { "Doc", "Grumpy", "Happy", "Sleepy", "Dopey", "Bashful", "Sneezy"};

static int say_the_line = 0;
static int hi_ho_received = 0;

int handle_fork(void (*)(int, const char *), int, const char *);
void wait_for_children(size_t);
void dwarf_sighandler(int);
void snowwhite_sighandler(int);
void snowwhite(int *);
void dwarf(int, const char *);

int main(void)
{
    int ppid = getpid();

    int children[N_DWARFS] = {0};
    for (int i = 0; i < N_DWARFS; ++i)
        children[i] = handle_fork(dwarf, ppid, dwarfs[i]);

    snowwhite(children);
    return 0;
}

int handle_fork(void (*childfunc)(int, const char *), int pid, const char *name)
{
    int cpid = fork();
    switch (cpid)
    {
        case 0:
            childfunc(pid, name);
            _exit(0);
        case -1:
            perror("fork");
            exit(1);
        default:
            return cpid;
    }
}

void wait_for_child(void)
{
    int status;
    int cpid = wait(&status);
    if (WEXITSTATUS(status))
    {
        fprintf(stderr, "ERROR: child %d exited with code %d\n", cpid, status);
        exit(1);
    }
}

void dwarf_sighandler(int sig)
{
    if (sig == SIGUSR1)
        say_the_line = 1;
}

void snowwhite_sighandler(int sig)
{
    if (sig == SIGUSR1)
        hi_ho_received = 1;
}

void dwarf(int ppid, const char *name)
{
    signal(SIGUSR1, dwarf_sighandler);
    printf("HELLO I'm %s, my pid is %d, and my parent's pid is %d\n", name, getpid(), ppid);

    while (!say_the_line);

    printf("%s HI,HO!!\n", name);

    kill(ppid, SIGUSR1);
}

void snowwhite(int *children)
{
    sleep(1);
    signal(SIGUSR1, snowwhite_sighandler);
    for (int i = 0; i < N_DWARFS; ++i)
    {
        printf("HI,HO!! %s\n", dwarfs[i]);
        kill(children[i], SIGUSR1);
        hi_ho_received = 0;
        while (!hi_ho_received);
        wait_for_child();
    }
}
